/*
 * Skin.h
 *
 *  Created on: 2012/03/15
 *      Author: takedanaoki
 */

#ifndef INCLUDED_NTENGINE_SCENE_SKIN_H
#define INCLUDED_NTENGINE_SCENE_SKIN_H

#include "NTEngine/Xml/Element.h"
#include "NTEngine/Xml/Attribute.h"
#include "NTEngine/Base/ReferenceCounter.h"
#include "NTEngine/Base/HashMap.h"
#include "NTEngine/Graphics/VertexBuffer.h"
#include "NTEngine/Math/Matrix3x4.h"
#include "NTEngine/Scene/Container.h"

#include "Scene/src/SkinNode.h"

namespace NTEngine {
using namespace Math;
namespace Scene{
class Node;

class Skin : public ReferenceCounter {
public:
	Skin( Xml::ConstElement&, Container::Impl& );
	~Skin() {
		mName = 0; //コンテナが持っている文字列なのでここでは参照をはずすだけ
		SAFE_DELETE_ARRAY( mWeights );
		SAFE_DELETE_ARRAY( mMatrixIndeices );
		SAFE_DELETE_ARRAY( mBones );
		SAFE_DELETE_ARRAY( mBuffer );
		// 後片付け
		for ( int i = 0; i < mNodeNumber; ++i ) {
			mNodes[ i ].~SkinNode();
		}
		OPERATOR_DELETE( mNodes );
		mContainer = 0;
	}
	void update() {
		Graphics::Vertex* vertices;
		mVertexBuffer.lock( &vertices );
//		Vertex* vertices = mVertexBuffer();
		for ( int i = 0; i < mVertexBuffer.vertexNumber(); ++i ) {
			const Graphics::Vertex& v = mBuffer[ i ];
			Vector4 px( 0 ), py( 0 ), pz( 0 );
			Vector3 nx( 0 ), ny( 0 ), nz( 0 );
			for ( int k = 0; k < mMaxWeightsPerVertex; ++k ) {
				int idx = matrixIndeices( i, k );
				if ( idx < 0 ) {
					break; // 終わり
				}
				ASSERT( idx < mBoneNumber );
				float weight = weights( i, k );
				const Matrix3x4& m = mBones[ idx ];
				Matrix3x4 it;
				it.setInverse( m );
				it.transpose33();
				px.madd( &m.m11, weight );
				py.madd( &m.m21, weight );
				pz.madd( &m.m31, weight );
				nx.madd( &it.m11, weight );
				ny.madd( &it.m21, weight );
				nz.madd( &it.m31, weight );
			}
			Graphics::Vertex& dst = vertices[ i ];
			dst.p.set( px * v.p, py * v.p, pz * v.p );
			dst.n.set( nx * v.n, ny * v.n, nz * v.n );
		}
		mVertexBuffer.unlock( &vertices );
	}
	const SkinNode* node( const char* name ) const {
		int p = mNodeMap.find( name );
		if ( mNodeMap.isEnd( p ) ){
			return 0;
		}else{
			int idx = *mNodeMap.value( p );
			return &mNodes[ idx ];
		}
	}
	float weights( int i, int k ) const {
		return mWeights[ i * mMaxWeightsPerVertex + k ];
	}
	int matrixIndeices( int i, int k ) const {
		return mMatrixIndeices[ i * mMaxWeightsPerVertex + k ];
	}
	int maxWeightsPerVertex() const {
		return mMaxWeightsPerVertex;
	}
	const char* name() const {
		return mName;
	}
	Container::Impl* container(){
		return mContainer;
	}
	Matrix3x4* bones() {
		return mBones;
	}
	// 頂点でやりやすいように整形
	void generate( Node* );
private:
	int mMaxWeightsPerVertex;
	float* mWeights;
	int* mMatrixIndeices;

	int mBoneNumber;
	Matrix3x4* mBones;

	int mNodeNumber;
	SkinNode* mNodes;
	HashMap< const char*, int > mNodeMap;

	const char* mName;
	Graphics::VertexBuffer mVertexBuffer; // 影響をあたえる頂点バッファ
	Graphics::Vertex* mBuffer; // 初期頂点バッファ

	Container::Impl* mContainer;
};

} // namespace Scene
} // namespace NTEngine

#endif // INCLUDED_NTENGINE_SCENE_SKINIMPL_H
